﻿using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using Lt.Engine.Data;
using Lt.Engine.Entity;
using Lt.Engine.Library;
using Lt.Engine.Sampling;
using Lt.Engine.Scene;
using Lt.Managers;
using RayDen.Library.Components.SystemComponents;
using RayDen.Library.Data.Import.OBJLoader;
using RayDen.Library.Entity.Frames;
using RayDen.Library.Entity.Scene;
using RayDen.Library.Entity.SceneGraph;
using RayDen.RayEngine.Data;
using RayDen.RayEngine.Samplers;
using RayDen.RayEngine.Scene;
using VolumeProfile = Lt.Engine.Data.VolumeProfile;

namespace Lt.Engine
{
    public class SceneAdapter
    {
        private LtScene scene;
        private SceneGeometryInfo sceneGeoInfo;
        private SceneDataInfo sceneData;
        public SceneAdapter(LtScene scn)
        {
            this.scene = scn;
        }

        public void Populate(int width, int height, SceneGraphInfo scg)
        {
            var svc = new SceneGraphService();
            svc.Populate(new GlobalIndexObjLoader(), new MaterialLoader());
            var cameras = new List<LtCamera>();
            var geometry = new List<LtGeoObject>();
            var lights = new List<LtLightsource>();
            var volumes = new List<LtVolume>();
            var solidMats = new List<SolidMaterial>();
            var matInfos = new List<MaterialInfo>();
            var lightInfos = new List<LightsourceInfo>();

            svc.SceneGraph.Visit((s) =>
            {
                if (s is GeometryGroupNode)
                {
                    sceneGeoInfo = (SceneGeometryInfo)scg.ResolveDataNode<SceneGeometryDataNode>(s as GeometryGroupNode).GetDataItem();
                }
                else if (s is LightsourceNode)
                {
                    var li = (LightsourceInfo)s.GetDataItem();
                    lightInfos.Add(li);
                }
                else if (s is CameraNode)
                {
                    var ci = (CameraInfo)s.GetDataItem();
                    cameras.Add(new LtCamera(new BasicPerspectiveCamera(ci.Position, ci.Direction, ci.Up, width, height, ci.Fov), ci));
                }
                else if (s is SceneMaterialDataNode)
                {
                    var mi = (MaterialInfo)s.GetDataItem();
                    matInfos.Add(mi);
                    Trace.TraceInformation("Loading textures for "+mi.Name);
                    Tracer.TraceLine("Loading textures for " + mi.Name);
                    foreach (var textureInfo in new[] { mi.Diffuse, mi.Alpha, mi.BumpMap, mi.NormalMap, mi.Specular })
                    {
                        TextureManager.Instance.LoadTexture(textureInfo);
                    }
                }
            });

            bool hasAreaLights = lightInfos.Any(item => item.Type == LightSourceTypes.Area);
            var solids = new Dictionary<string, SolidMaterial>();
            //Materials building 
            foreach (var materialInfo in matInfos)
            {
                var bsdf = SolidMaterialFactory.Instance.CreateMaterial(materialInfo);
                bsdf.Alpha = TextureManager.Instance.GetByInfo(materialInfo.Alpha);
                bsdf.Diffuse = TextureManager.Instance.GetByInfo(materialInfo.Diffuse);
                bsdf.Specular = TextureManager.Instance.GetByInfo(materialInfo.Specular);
                bsdf.Bump = TextureManager.Instance.GetByInfo(materialInfo.BumpMap);
                bsdf.Normal = TextureManager.Instance.GetByInfo(materialInfo.NormalMap);
                solidMats.Add(bsdf);
                bsdf.scene = scene;
                solids.Add(materialInfo.Name.ToLowerInvariant(), bsdf);
            }
            //Geometry
            foreach (var triangleMeshInfo in sceneData.Meshes)
            {
                if (VolumeSampler.IsVolume(triangleMeshInfo))
                {
                    var volumeObject = new LtVolume(new MeshShape(triangleMeshInfo), new VolumeProfile());
                    volumes.Add(volumeObject);
                }
                else
                {
                    var geoObject = new LtGeoObject(triangleMeshInfo,
                        solids[triangleMeshInfo.MaterialName.ToLowerInvariant()]);
                    geometry.Add(geoObject);
                }
                /*
                if (hasAreaLights)
                {
                    if (LightSampler.IsLight(triangleMeshInfo.MeshName))
                    {
                        
                    }
                }
                 */
            }

            //Lights

            foreach (var lightsourceInfo in lightInfos)
            {
                switch (lightsourceInfo.Type)
                {
                    case LightSourceTypes.Point:
                        lights.Add(new LtLightsource(new PointLightsource(), new EmissionProfile()));
                        break;
                }
            }

        }


    }
}